
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <rtthread.h>

#include "rtdevice.h"
#include "at.h"

#include "bsp.h"
#include "net_trans.h"
#include "ws_client.h"
#include "phy_trans.h"

/* 栈大小 */
#define NET_STACK_SIZE				1024
/* 优先级 */
#define NET_THREAD_PRIORITY			24
/* 时间片大小 */
#define NET_THREAD_TIMESLICE		5

/* L306串口接收缓存最大长度 */
#define NET_UART_MAX_LEN			512
/* AT指令应答最大长度 */
#define AT_RESP_MAX_LEN				128 
/* AT指令最长响应时间 */
#define AT_RESP_MAX_TIME			rt_tick_from_millisecond(30 * 1000)

/* websocket应答超时时间 */
#define WS_RESP_TIMEOUT				rt_tick_from_millisecond(10 * 1000)

static const char *ip = "121.40.70.187";
static const char *port = "8888";
//static const char *port = "8999";

static void net_start_async_recv(void);
static void net_stop_async_recv(void);
static void net_uart_recv_timeout_func(const char *buf, size_t size);

ALIGN(RT_ALIGN_SIZE)
static char net_thread_stack[NET_STACK_SIZE];
static struct rt_thread net_thread;

/* 存储应答结构指针 */
static at_response_t atrsp;

/* 存储IMEI */
static char IMEI[20] = {0};

/* 存储TCP连接状态 */
static enum net_status netstatus = NET_DISCONNECT;

/* 用于同步net_trans线程信号量 */
static rt_sem_t net_sem = RT_NULL;
/* websocket解析封装使用缓存 */
static char ws_buffer[NET_UART_MAX_LEN];
/* 是否正在进行websocket握手 */
static bool ws_is_handshaking = false;

/* 开启L306串口异步接收, TCP连接成功后且启动CIPMODE后使用 */
static void net_start_async_recv(void)
{
	at_set_recv_timeout_func(net_uart_recv_timeout_func);
}

/* 认为TCP连接已经断开时，退出异步接收串口数据 */
static void net_stop_async_recv(void)
{
	at_client_send("+++\r\n", 5);
	at_unset_recv_timeout_func();
	netstatus = NET_DISCONNECT;
}

/* 透传模式下L306串口接收超时回调数据 */
static void net_uart_recv_timeout_func(const char *buf, size_t size)
{
	/* 处理TCP连接被服务器主动断开的情况 */
	if (buf[0] == 0x0d && size > 6)
	{
		for (int i=0; i<size-6; i++)
		{
			if (memcmp(buf+i, "CLOSED", 6) == 0)
			{
				net_stop_async_recv();
				rt_kprintf("TCP CLOSED!!!\n");
				rt_sem_release(net_sem);
				return;
			}
		}
	}

	/* 处理TCP数据 */
	if (ws_is_handshaking)
	{
		if (ws_check_handshake_rsp(buf))
		{
			rt_sem_release(net_sem);
		}
		else
		{
			rt_kprintf("parse handshake rsp failed: \n");
			rt_kprintf("%s", buf);
		}
	}
	else
	{
		/* 解析websocket数据 */
		struct ws_frame frame;
		frame.payload = ws_buffer;

		if (ws_parse_recv_frame(&frame, buf, size))
		{
			if (frame.opcode == WS_FRAME_TXT)
			{
				rt_kprintf("%s", frame.payload);

				if (memcmp(frame.payload, "Login 0\r\n0", 10) == 0)
				{
					rt_sem_release(net_sem);
				}
			}
			else if (frame.opcode == WS_FRAME_BINARY)
			{
				/* 将数据透传给电表 */
				rt_kprintf("send %d bytes net data to phy..\n", frame.payloadlen);

				for (int i=0; i<frame.payloadlen; i++)
				{
					rt_kprintf("%02x ", frame.payload[i]);
				}
				rt_kprintf("\n");

				phy_send(frame.payload, frame.payloadlen);
			}
			else if (frame.opcode == WS_FRAME_PONG)
			{
				rt_sem_release(net_sem);
			}
		}
		else
		{
			rt_kprintf("parse ws data failed..\n");
			for (int i=0; i<size; i++)
			{
				rt_kprintf("%02x ", buf[i]);
			}
			rt_kprintf("\n");
		}
	}
}

/* 线程入口 */
static void net_thread_entry(void *param)
{
	rt_err_t at_err;

	/* 重启L306模块, 并等待TCPIP OK */
    rt_kprintf("waiting tcpip ok...\n");
	bsp_l306_reset();

	/* 如果60s没有收到TCPIP OK, 认为L306启动失败 */
	if (rt_sem_take(net_sem, rt_tick_from_millisecond(60 * 1000)) != RT_EOK)
	{
		/* 握手超时 */
		rt_kprintf("wait tcpip ok timeout!!!\n");
		return;
	}

    rt_kprintf("recved tcpip ok!\n");

	/* 检查sim卡状态 */
	while (1)
	{
		at_err = at_exec_cmd(atrsp, "AT+CPIN?");

		if (at_err == -2)
		{
			rt_kprintf("at+cpin no rsp\n");
			rt_thread_mdelay(3 * 1000);
			continue;
		}

		if (at_resp_get_line_by_kw(atrsp, "READY") != NULL)
		{
			at_exec_cmd(atrsp, "ATE0");
			rt_kprintf("sim card ok\n");			
			break;
		}
		else
		{
			rt_kprintf("sim card err\n");			
			return;
		}
	}

	/*读取IMEI */
	at_resp_set_info(atrsp, AT_RESP_MAX_LEN, 4, rt_tick_from_millisecond(5000));
	if (at_exec_cmd(atrsp, "AT+CGSN") == RT_EOK)
	{
		const char *imei = at_resp_get_line(atrsp, 2);
		RT_ASSERT(strlen(imei) < sizeof(IMEI));

		memset(IMEI, 0, sizeof(IMEI));
		memcpy(IMEI, imei, 15);

		rt_kprintf("read %u bytes IMEI: %s\n", strlen(IMEI), IMEI);
	}
	else
	{
		rt_kprintf("read IMEI failed..\n");
		RT_ASSERT(0);
	}
	at_resp_set_info(atrsp, AT_RESP_MAX_LEN, 0, AT_RESP_MAX_TIME);

	/* 检查网络状态 */
	while (1)
	{
		if (at_exec_cmd(atrsp, "AT+CREG?") == RT_EOK
			&& at_resp_get_line_by_kw(atrsp, "+CREG: 0,1") != NULL)
		{
			/* 附着基站成功 */
			rt_kprintf("L306 attach successful\n");
			break;
		}
		else
		{
			rt_kprintf("L306 attaching...\n");
		}

		rt_thread_mdelay(10 * 1000);
	}

	/* 设置apn */
	at_exec_cmd(atrsp, "AT+CSTT=\"UNIM2M.GZM2MAPN\",\"IP\",\"\",\"\"");
	// at_exec_cmd(atrsp, "AT+CSTT=\"airtelgprs.com\"");

	/* 维持TCP连接 */
	while(1)
	{
		if (netstatus == NET_DISCONNECT)
		{
			at_resp_set_info(atrsp, AT_RESP_MAX_LEN, 2, rt_tick_from_millisecond(5000));
			at_exec_cmd(atrsp, "AT+CIPSHUT");
			at_resp_set_info(atrsp, AT_RESP_MAX_LEN, 0, AT_RESP_MAX_TIME);

			/* 等待激活PDP场景成功 */
			at_err = at_exec_cmd(atrsp, "AT+CIICR");

			if (at_err == RT_EOK)
			{
				rt_kprintf("ciicr ok!\n");
			}
			else
			{
				rt_thread_mdelay(3000);
				rt_kprintf("ciicr failed!\n");
				continue;
			}

			at_exec_cmd(atrsp, "AT+CGDATA=\"M-MBIM\",1,1");
			at_exec_cmd(atrsp, "AT+CIPMUX=0");

			at_resp_set_info(atrsp, AT_RESP_MAX_LEN, 1, AT_RESP_MAX_TIME);
			at_exec_cmd(atrsp, "AT+CIPSTART=\"TCP\",\"%s\",\"%s\"", ip, port);
			
			/* 等待TCP连接成功，失败或者超时 */
			if (rt_sem_take(net_sem, rt_tick_from_millisecond(30 * 1000)) != RT_EOK)
			{
				rt_kprintf("cipstart rsp timeout...\n");
				continue;
			}
			else
			{
				if (netstatus == NET_CONNECTED)
				{
					rt_kprintf("tcp connect ok!\n");

					/* 进入单连接透传模式 */
					at_resp_set_info(atrsp, AT_RESP_MAX_LEN, 0, AT_RESP_MAX_TIME);
					if (at_exec_cmd(atrsp, "AT+CIPMODE") == RT_EOK)
					{
						rt_kprintf("cipmode ok!");
						netstatus = NET_CONNECTED;
						net_start_async_recv();
						continue;
					}
					else
					{
						rt_kprintf("cip mode failed!\n");
					}
				}
				else
				{
					rt_kprintf("tcp connect failed...\n");
				}
			}

			rt_thread_mdelay(10 * 1000);
		}
		else
		{
			size_t send_len = 0, len = 0;
			char payload[30] = {0};
			struct ws_frame frame;

			/* websocket握手 */
			send_len = ws_build_handshake_req(ws_buffer, sizeof(ws_buffer), ip, port, "/");
			RT_ASSERT(send_len > 0);

			ws_is_handshaking = true;
			at_client_send(ws_buffer, send_len);
			if (rt_sem_take(net_sem, WS_RESP_TIMEOUT) != RT_EOK)
			{
				/* 握手超时 */
				rt_kprintf("ws handshake rsp timeout!!!\n");
				net_stop_async_recv();
				continue;
			}
			ws_is_handshaking = false;
			rt_kprintf("ws handshake success...\n");

			/* 登录服务器 */
			len = snprintf(payload, sizeof(payload), "Login 0\r\n%s\r\n", IMEI);
			RT_ASSERT(len > 0);

			frame.opcode = WS_FRAME_TXT;
			frame.payload = payload;
			frame.payloadlen = len;
			send_len = ws_build_frame(&frame, ws_buffer, sizeof(ws_buffer));
			RT_ASSERT(send_len > 0);

			at_client_send(ws_buffer, send_len);
			if (rt_sem_take(net_sem, WS_RESP_TIMEOUT) != RT_EOK)
			{
				/* 登录超时 */
				rt_kprintf("ws login timeout!!!\n");
				net_stop_async_recv();
				continue;
			}
			rt_kprintf("ws login success...\n");

			/* 周期发送保活数据 */
			while(1)
			{
				frame.opcode = WS_FRAME_PING;
				frame.payload = "eq";
				frame.payloadlen = 2;
				send_len = ws_build_frame(&frame, ws_buffer, sizeof(ws_buffer));
				RT_ASSERT(send_len > 0);

				at_client_send(ws_buffer, send_len);
				if (rt_sem_take(net_sem, WS_RESP_TIMEOUT) != RT_EOK)
				{
					/* ping 超时 */
					rt_kprintf("ws ping timeout!!!\n");
					net_stop_async_recv();
					break;
				}
				rt_kprintf("ws ping ok...\n");

				/* 等待周期发送数据 */
				if (rt_sem_take(net_sem, rt_tick_from_millisecond(3 * 60 * 1000)) == RT_EOK)
				{
					break;
				}
			}
		}
	}
}

static void urc_init_done_func(const char *data, rt_size_t size)
{
	rt_sem_release(net_sem);
}

static void urc_connect_ok_func(const char *data, rt_size_t size)
{
	netstatus = NET_CONNECTED;
	rt_sem_release(net_sem);
}

static void urc_connect_fail_func(const char *data, rt_size_t size)
{
	netstatus = NET_DISCONNECT;
	rt_sem_release(net_sem);
}

static struct at_urc urc_table[] = 
{
    {"TCPIP OK", "\r\n", urc_init_done_func},
    {"CONNECT OK", "\r\n", urc_connect_ok_func},
    {"CONNECT FAIL", "\r\n", urc_connect_fail_func},
};

void net_trans_start(void)
{
	int ret = at_client_init("uart2", NET_UART_MAX_LEN);
	RT_ASSERT(ret == RT_EOK);

    at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));

	atrsp = at_create_resp(AT_RESP_MAX_LEN, 0, rt_tick_from_millisecond(3 * 1000));
	RT_ASSERT(atrsp != NULL);

	net_sem = rt_sem_create("net_sem", 0, RT_IPC_FLAG_FIFO);
	RT_ASSERT(net_sem != NULL);

    rt_thread_init(&net_thread,
                   "net_thread",
                   net_thread_entry,
                   RT_NULL,
                   &net_thread_stack[0],
                   sizeof(net_thread_stack),
                   NET_THREAD_PRIORITY - 1, NET_THREAD_TIMESLICE);

    rt_thread_startup(&net_thread);
}

enum net_status net_get_status(void)
{
	return netstatus;
}

void net_send(char *buffer, rt_size_t len)
{
	size_t send_len;
	struct ws_frame frame;
	rt_size_t singleSendLen = 300;

	frame.opcode = WS_FRAME_BINARY;
	frame.payload = buffer;
	frame.payloadlen = len;

	send_len = ws_build_frame(&frame, ws_buffer, sizeof(ws_buffer));
	RT_ASSERT(send_len > 0);

	at_client_send(ws_buffer, send_len);
}

